export const hashRE = /#.*$/;
export const extRE = /\.(md|html)$/;
export const endingSlashRE = /\/$/;
export const outboundRE = /^[a-z]+:/i;

export function normalize(path) {
	return decodeURI(path).replace(hashRE, '').replace(extRE, '');
}

export function getHash(path) {
	const match = path.match(hashRE);
	if (match) {
		return match[0];
	}
}

export function isExternal(path) {
	return outboundRE.test(path);
}

export function isMailto(path) {
	return /^mailto:/.test(path);
}

export function isTel(path) {
	return /^tel:/.test(path);
}

export function ensureExt(path) {
	if (isExternal(path)) {
		return path;
	}
	const hashMatch = path.match(hashRE);
	const hash = hashMatch ? hashMatch[0] : '';
	const normalized = normalize(path);

	if (endingSlashRE.test(normalized)) {
		return path;
	}
	return normalized + '.html' + hash;
}

export function isActive(route, path) {
	const routeHash = decodeURIComponent(route.hash);
	const linkHash = getHash(path);
	if (linkHash && routeHash !== linkHash) {
		return false;
	}
	const routePath = normalize(route.path);
	const pagePath = normalize(path);
	return routePath === pagePath;
}

export function resolvePage(pages, rawPath, base) {
	if (isExternal(rawPath)) {
		return {
			type: 'external',
			path: rawPath,
		};
	}
	if (base) {
		rawPath = resolvePath(rawPath, base);
	}
	const path = normalize(rawPath);
	for (let i = 0; i < pages.length; i++) {
		if (normalize(pages[i].regularPath) === path) {
			return Object.assign({}, pages[i], {
				type: 'page',
				path: ensureExt(pages[i].path),
			});
		}
	}
	console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`);
	return {};
}

function resolvePath(relative, base, append) {
	const firstChar = relative.charAt(0);
	if (firstChar === '/') {
		return relative;
	}

	if (firstChar === '?' || firstChar === '#') {
		return base + relative;
	}

	const stack = base.split('/');

	// remove trailing segment if:
	// - not appending
	// - appending to trailing slash (last segment is empty)
	if (!append || !stack[stack.length - 1]) {
		stack.pop();
	}

	// resolve relative path
	const segments = relative.replace(/^\//, '').split('/');
	for (let i = 0; i < segments.length; i++) {
		const segment = segments[i];
		if (segment === '..') {
			stack.pop();
		} else if (segment !== '.') {
			stack.push(segment);
		}
	}

	// ensure leading slash
	if (stack[0] !== '') {
		stack.unshift('');
	}

	return stack.join('/');
}

/**
 * @param { Page } page
 * @param { string } regularPath
 * @param { SiteData } site
 * @param { string } localePath
 * @returns { SidebarGroup }
 */
export function resolveSidebarItems(page, regularPath, site, localePath) {
	const { pages, themeConfig } = site;

	const localeConfig = localePath && themeConfig.locales ? themeConfig.locales[localePath] || themeConfig : themeConfig;

	const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar;
	if (pageSidebarConfig === 'auto') {
		return resolveHeaders(page);
	}

	const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar;
	if (!sidebarConfig) {
		return [];
	} else {
		const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig);
		if (config === 'auto') {
			return resolveHeaders(page);
		}
		return config ? config.map((item) => resolveItem(item, pages, base)) : [];
	}
}

/**
 * @param { Page } page
 * @returns { SidebarGroup }
 */
function resolveHeaders(page) {
	const headers = groupHeaders(page.headers || []);
	return [
		{
			type: 'group',
			collapsable: false,
			title: page.title,
			path: null,
			children: headers.map((h) => ({
				type: 'auto',
				title: h.title,
				basePath: page.path,
				path: page.path + '#' + h.slug,
				children: h.children || [],
			})),
		},
	];
}

export function groupHeaders(headers) {
	// group h3s under h2
	headers = headers.map((h) => Object.assign({}, h));
	let lastH2;
	headers.forEach((h) => {
		if (h.level === 2) {
			lastH2 = h;
		} else if (lastH2) {
			(lastH2.children || (lastH2.children = [])).push(h);
		}
	});
	return headers.filter((h) => h.level === 2);
}

export function resolveNavLinkItem(linkItem) {
	return Object.assign(linkItem, {
		type: linkItem.items && linkItem.items.length ? 'links' : 'link',
	});
}

/**
 * @param { Route } route
 * @param { Array<string|string[]> | Array<SidebarGroup> | [link: string]: SidebarConfig } config
 * @returns { base: string, config: SidebarConfig }
 */
export function resolveMatchingConfig(regularPath, config) {
	if (Array.isArray(config)) {
		return {
			base: '/',
			config: config,
		};
	}
	for (const base in config) {
		if (ensureEndingSlash(regularPath).indexOf(encodeURI(base)) === 0) {
			return {
				base,
				config: config[base],
			};
		}
	}
	return {};
}

function ensureEndingSlash(path) {
	return /(\.html|\/)$/.test(path) ? path : path + '/';
}

function resolveItem(item, pages, base, groupDepth = 1) {
	if (typeof item === 'string') {
		return resolvePage(pages, item, base);
	} else if (Array.isArray(item)) {
		return Object.assign(resolvePage(pages, item[0], base), {
			title: item[1],
		});
	} else {
		const children = item.children || [];

		if (item.type === 'divider') {
			return { type: 'divider', path: '' };
		}

		if (children.length === 0 && item.path) {
			return Object.assign(resolvePage(pages, item.path, base), {
				title: item.title,
			});
		}
		return {
			type: 'group',
			path: item.path,
			title: item.title,
			sidebarDepth: item.sidebarDepth,
			initialOpenGroupIndex: item.initialOpenGroupIndex,
			children: children.map((child) => resolveItem(child, pages, base, groupDepth + 1)),
			collapsable: item.collapsable !== false,
		};
	}
}
